package com.hzw.net.utils

import android.util.Base64
import java.nio.charset.StandardCharsets
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException

object RSAUtils {

    private const val RSA = "RSA"
    private const val PEM = "RSA/ECB/PKCS1Padding"
    private const val PUBLIC_KEY_BEGIN = "-----BEGIN PUBLIC KEY-----"
    private const val PUBLIC_KEY_END = "-----END PUBLIC KEY-----"
    private const val PRIVATE_KEY_BEGIN = "-----BEGIN PRIVATE KEY-----"
    private const val PRIVATE_KEY_END = "-----END PRIVATE KEY-----"

    /**
     * 随机生成RSA密钥对(默认密钥长度为1024)
     *
     * @return
     */
    fun generateRSAKeyPair(): KeyPair? {
        return generateRSAKeyPair(1024)
    }

    /**
     * 生成用于传输的公钥
     * 两次Base64加密
     * @param publicKey 公钥
     * @return 传输的公钥字符串
     */
    fun getPublicKeyStandardStringForSend(publicKey: PublicKey): String {
        val key = getPublicKeyStandardString(publicKey)
        val encode = Base64.encode(
            key.toByteArray(StandardCharsets.UTF_8),
            Base64.NO_WRAP
        )
        return String(encode, StandardCharsets.UTF_8)
    }

    /**
     * 获取标准的公钥字符串
     * 带-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----
     * @param publicKey 公钥
     * @return 标准公钥字符串
     */


    fun getPublicKeyStandardString(publicKey: PublicKey): String {
        var standardKey = PUBLIC_KEY_BEGIN + "\n"
        standardKey += getKeyString(publicKey) + "\n" + PUBLIC_KEY_END
        return standardKey.trim()
    }

    /**
     * 获取key的字符串
     * @param key 可以
     * @return key的字符串
     */
    fun getKeyString(key: Key): String? {
        return String(
            Base64.encode(key.encoded, Base64.NO_WRAP),
            StandardCharsets.UTF_8
        )
    }


    /**
     * 格式化公钥
     * 去掉头尾
     * @param publicKey 公钥字符串
     * @return 公钥
     */
    fun formatPublicKey(publicKey: String): String? {
        return publicKey.replace(PUBLIC_KEY_BEGIN, "").replace(PUBLIC_KEY_END, "").trim()
    }

    /**
     * 随机生成RSA密钥对
     *
     * @param keyLength 密钥长度，范围：512～2048<br></br>
     * 一般1024
     * @return
     */
    fun generateRSAKeyPair(keyLength: Int): KeyPair? {
        return try {
            val kpg = KeyPairGenerator.getInstance(RSA)
            kpg.initialize(keyLength)
            kpg.genKeyPair()
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        }
    }


    fun encryptRSA2Base64String(
        data: String,
        key: ByteArray,
        isPublicKey: Boolean
    ): String {
        return String(encryptRSA2Base64(data.toByteArray(), key, isPublicKey))
    }


    /**
     * rsa加密，并base64之后返回
     * @param data 原始数据
     * @param key 秘钥
     * @param isPublicKey 是否公钥
     * @return
     */
    fun encryptRSA2Base64(
        data: ByteArray,
        key: ByteArray,
        isPublicKey: Boolean
    ): ByteArray {
        return Base64.encode(
            rsaTemplate(data, key, isPublicKey, true),
            Base64.NO_WRAP
        )
    }

    /**
     * rsa解密
     * @param data 原始数据
     * @param key 秘钥
     * @param isPublicKey 是否公钥
     * @return
     */
    fun decryptBase64RSA(
        data: ByteArray?,
        key: ByteArray?,
        isPublicKey: Boolean
    ): ByteArray? {
        return rsaTemplate(
            Base64.decode(data, Base64.NO_WRAP),
            key,
            isPublicKey,
            false
        )
    }

    private fun rsaTemplate(
        data: ByteArray?,
        key: ByteArray?,
        isPublicKey: Boolean,
        isEncrypt: Boolean
    ): ByteArray? {
        if (data == null || data.isEmpty() || key == null || key.isEmpty()) {
            return null
        }
        try {
            val rsaKey: Key?
            rsaKey = if (isPublicKey) {
                val keySpec =
                    X509EncodedKeySpec(key)
                KeyFactory.getInstance("RSA").generatePublic(keySpec)
            } else {
                val keySpec =
                    PKCS8EncodedKeySpec(key)
                KeyFactory.getInstance("RSA").generatePrivate(keySpec)
            }
            if (rsaKey == null) return null
            val cipher = Cipher.getInstance(PEM)
            cipher.init(
                if (isEncrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE,
                rsaKey
            )
            val len = data.size
            val maxLen = if (isEncrypt) 117 else 128
            val count = len / maxLen
            return if (count > 0) {
                var ret = ByteArray(0)
                var buff = ByteArray(maxLen)
                var index = 0
                for (i in 0 until count) {
                    System.arraycopy(data, index, buff, 0, maxLen)
                    ret = joins(ret, cipher.doFinal(buff))
                    index += maxLen
                }
                if (index != len) {
                    val restLen = len - index
                    buff = ByteArray(restLen)
                    System.arraycopy(data, index, buff, 0, restLen)
                    ret = joins(ret, cipher.doFinal(buff))
                }
                ret
            } else {
                cipher.doFinal(data)
            }
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: NoSuchPaddingException) {
            e.printStackTrace()
        } catch (e: InvalidKeyException) {
            e.printStackTrace()
        } catch (e: BadPaddingException) {
            e.printStackTrace()
        } catch (e: IllegalBlockSizeException) {
            e.printStackTrace()
        } catch (e: InvalidKeySpecException) {
            e.printStackTrace()
        }
        return null
    }

    private fun joins(prefix: ByteArray, suffix: ByteArray): ByteArray {
        val ret = ByteArray(prefix.size + suffix.size)
        System.arraycopy(prefix, 0, ret, 0, prefix.size)
        System.arraycopy(suffix, 0, ret, prefix.size, suffix.size)
        return ret
    }
}